/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package calculatorParser;

import intervalMath.*;
import java.util.*;

public class stackDeikstri {
    static public String[] functions = {"cos", "sin", "ln", "exp", "sqrt", "dual", "pro", "opp", "inv"};
    static public String[] operators = {"+", "-", "/", "*", "inf", "sup"};

    static public List<String> generateRPN(List<String> lexemes) throws Exception {
        List<String> rpn = new LinkedList<String>();
        Stack<String> operatorsStack = new Stack<String>();
        for(String lexem: lexemes) {
            if (isArrayContains(operators, lexem)) {
                while (!operatorsStack.isEmpty()
                    && priority(operatorsStack.peek()) > priority(lexem)
                ) {
                    rpn.add(operatorsStack.pop());
                }
                operatorsStack.push(lexem);
            } else if (lexem.equals("(") || isArrayContains(functions, lexem)) {
                operatorsStack.push(lexem);
            } else if (lexem.equals(")")) {
                if (operatorsStack.isEmpty()) {
                    throw new Exception("Скобки не содержат выражения!");
                }
                while (!operatorsStack.peek().equals("(")) {
                    rpn.add(operatorsStack.pop());
                    if (operatorsStack.isEmpty()) {
                        throw new Exception("Отсутствует открывающая скобка!");
                    }
                }
                operatorsStack.pop();
                if (!operatorsStack.isEmpty() && isArrayContains(functions, operatorsStack.peek())) {
                    rpn.add(operatorsStack.pop());
                }
            } else {
                //here? it means lexem is interval
                rpn.add(lexem);
            }
        }
        while(!operatorsStack.isEmpty()) {
            if (operatorsStack.peek().equals("(")) {
                throw new Exception("Отсутствует закрывающая скобка!");
            }
            rpn.add(operatorsStack.pop());
        }
        return rpn;
    }

    static public boolean isArrayContains(String[] array, String str) {
        for(String item: array) {
            if (item.equals(str)) {
                return true;
            }
        }
        return false;
    }

    static private KaucherIntervalArithmetic calcResult(
        String operation,
        KaucherIntervalArithmetic operand
    ) {
        KaucherIntervalArithmetic ka = new KaucherIntervalArithmetic();
        if (operation.equals("sin"))
            ka = (KaucherIntervalArithmetic)IntervalElementaryMath.sin(operand, 6);
        if (operation.equals("cos"))
            ka = (KaucherIntervalArithmetic)IntervalElementaryMath.cos(operand, 6);
        if (operation.equals("exp"))
            ka = (KaucherIntervalArithmetic)IntervalElementaryMath.exp(operand, 6);
        if (operation.equals("ln"))
            ka = (KaucherIntervalArithmetic)IntervalElementaryMath.ln(operand, 6);
        if (operation.equals("sqrt"))
            ka = (KaucherIntervalArithmetic)IntervalElementaryMath.sqrt(operand, 6);
        if (operation.equals("dual"))
            ka = (KaucherIntervalArithmetic)operand.dual();
        if (operation.equals("pro"))
            ka = (KaucherIntervalArithmetic)operand.pro();
        if (operation.equals("opp"))
            ka = (KaucherIntervalArithmetic)operand.opp();
        if (operation.equals("inv"))
            ka = (KaucherIntervalArithmetic)operand.inv(6);
        return ka;
    }

    static private KaucherIntervalArithmetic calcResult(
        String operation,
        KaucherIntervalArithmetic a,
        KaucherIntervalArithmetic b
    ) {
        KaucherIntervalArithmetic ka = new KaucherIntervalArithmetic();
        if (operation.equals("+"))
            ka = (KaucherIntervalArithmetic)a.add(b);
        if (operation.equals("-"))
            ka = (KaucherIntervalArithmetic)b.sub(a);
        if (operation.equals("*"))
            ka = (KaucherIntervalArithmetic)b.mult(a);
        if (operation.equals("/"))
            ka = (KaucherIntervalArithmetic)b.div(a, 5);
        if (operation.equals("inf"))
            ka = (KaucherIntervalArithmetic)b.inf(a);
        if (operation.equals("sup"))
            ka = (KaucherIntervalArithmetic)b.sup(a);
        return ka;
    }

    static public IntervalArithmetic fromRPNToInterval(List<String> rpn) throws Exception{
        Stack<String> bufferStack = new Stack<String>();
        Stack<String> mainStack = new Stack<String>();
        Object[] rpnArray = rpn.toArray();
        for (int i = rpnArray.length-1; i>=0; i--){
            mainStack.push(rpnArray[i].toString());
        }
        while (!mainStack.empty()){
            String lex = mainStack.pop();
            boolean isUnarLexem = isArrayContains(functions, lex);
            boolean isBinarLexem = isArrayContains(operators, lex);
            if (isUnarLexem){
                if(bufferStack.isEmpty()) throw new Exception("Неправильный формат выражения!");
                String operand = bufferStack.pop();
                KaucherIntervalArithmetic ki = new KaucherIntervalArithmetic(operand);
                KaucherIntervalArithmetic res = calcResult(lex, ki);
                mainStack.push(res.toString());
            }else if(isBinarLexem){
                if(bufferStack.isEmpty()) throw new Exception("Неправильный формат выражения!");
                String operand = bufferStack.pop();
                KaucherIntervalArithmetic a = new KaucherIntervalArithmetic(operand);
                if(bufferStack.isEmpty()) throw new Exception("Неправильный формат выражения!");
                operand = bufferStack.pop();
                KaucherIntervalArithmetic b = new KaucherIntervalArithmetic(operand);
                KaucherIntervalArithmetic res = calcResult(lex, a, b);
                mainStack.push(res.toString());
            }else{
                bufferStack.push(lex);
            }
        }

        if(bufferStack.isEmpty() || bufferStack.size() > 1) 
            throw new Exception("Неправильный формат выражения!");
        return  new KaucherIntervalArithmetic(bufferStack.pop());
    }
    
    static private int priority(String s) {
        int res = 1;
        if (s.equals("*") || s.equals("/")) {
            res = 2;
        }
        return res;
    }
}
